iT邦幫忙

0

day25 服務辨識基礎

  • 分享至 

  • xImage
  •  

服務辨識 (Service Identification / Service Fingerprinting)
在網路安全與滲透測試中,服務辨識指的是:
確定目標主機上運行的 服務類型與版本。
例如:
目標的 80 port 開啟 → 服務可能是 HTTP
目標的 22 port 開啟 → 服務可能是 SSH
目標的 21 port 開啟 → 服務可能是 FTP
進一步辨識版本,可以得知:
服務版本(例如 Apache 2.4.52、OpenSSH 8.9)
作業系統平台(Linux, Windows)潛在漏洞(漏洞掃描要用到)

常用方法大致可分兩類:
1.Banner Grabbing(橫幅抓取)
原理:服務啟動時通常會回傳「歡迎訊息」,其中包含服務名稱和版本。
例子:
HTTP → Server: Apache/2.4.52 (Ubuntu)
FTP → 220 ProFTPD 1.3.5 Server (Debian)
2.工具/程式:
telnet 目標IP 服務Port
nc (Netcat)
Java Socket 自製簡單 Scanner

服務辨識:
確認目標機器某個開放 port 上真正運行的是什麼服務(HTTP/SSH/FTP…)以及可能的版本(例:Apache/2.4.52)。
目的:找出攻擊面(可利用的漏洞)、或確認不應該存在的服務以做資安防護。
兩大類方法:
被動 / 被動式 (Passive):監聽網路流量、抓已存在封包(不主動連線)。風險小、隱匿性高,但需權限/流量。
主動 / 主動式 (Active):向目標發送封包並分析回應(ex: Banner grabbing、Nmap -sV)。效果好但會留下痕跡。

Banner Grabbing(橫幅抓取)
原理:
很多服務在建立 TCP 連線時會回傳「歡迎訊息」或「回應 header」,裡面可能包含軟體名稱與版本。抓到後用正則或字典比對,就能判斷版本。
常見服務與可能的 Banner 範例
HTTP: Server: Apache/2.4.52 (Ubuntu) 或 Server: nginx/1.18.0 (Ubuntu)
SSH: SSH-2.0-OpenSSH_8.2p1 Ubuntu-4ubuntu0.3
FTP: 220 (vsFTPd 3.0.3)
SMTP: 220 mail.example.com ESMTP Postfix
優點 / 缺點
優點:實作簡單,速度快。
缺點:許多系統會關閉或偽造 Banner(例如將 Server 改為 nginx 或完全刪除),因此不可靠。

學到的主要觀念
Port 掃描只是第一步:
前幾天你學的是「某個 Port 開還是關 (OPEN/CLOSED)」。
但這還不足夠,因為知道「開了」還要知道「開的是什麼服務」。
服務辨識 (Service Identification):
這段程式教你如何在確認 Port 開啟後,嘗試連線並讀取 伺服器回應(banner 或 HTTP header)。
例如:
Port 80 → 可能是 Apache、Nginx、或 IIS
Port 443 → 可能是 gws (Google Web Server)
Port 25 → 可能會顯示 220 mail.example.com ESMTP Postfix
是資安裡很常用的 Banner Grabbing 技術。

package day1;
import java.io.;
import java.net.
;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.*;
import javax.net.ssl.HttpsURLConnection;

public class Day25Demo {
private static final int START_PORT = 1;
private static final int END_PORT = 1024;
private static final int THREADS = 20;
private static final int TIMEOUT_MS = 2000;

public static void main(String[] args) {
    if (args.length < 1) {
        System.out.println("Usage: java Day25Demo <host> [output.csv]");
        System.exit(1);
    }
    String host = args[0];
    String outFile = args.length >= 2 ? args[1] : "scan_result.csv";
    // ====== 建立執行緒池與提交任務(替換原有同類區段) ======
    ExecutorService pool = Executors.newFixedThreadPool(THREADS);

// 用一個佇列收集結果(如果你已有 results 佇列就不用再宣告)
BlockingQueue results = new LinkedBlockingQueue<>();
results.add("timestamp,host,port,status,banner");

// 提交任務(確保所有 submit 都在這裡完成)
for (int port = START_PORT; port <= END_PORT; port++) {
final int p = port;
pool.submit(() -> {
String timestamp = timestampNow();
String status = "";
String banner = "";

            try {
                if (p == 80) {
                    banner = httpHead(host, false);
                    status = "OPEN";
                } else if (p == 443) {
                    banner = httpHead(host, true);
                    status = "OPEN";
                } else {
                    try (Socket sock = new Socket()) {
                        sock.connect(new InetSocketAddress(host, p), TIMEOUT_MS);
                        sock.setSoTimeout(TIMEOUT_MS);
                        BufferedReader reader = new BufferedReader(new InputStreamReader(sock.getInputStream()));
                        String line = reader.readLine();
                        banner = line != null ? line.trim() : "";
                        status = "OPEN";
                    } catch (SocketTimeoutException ste) {
                        status = "TIMEOUT";
                        banner = "";
                    } catch (IOException ioe) {
                        status = "CLOSED";
                        banner = "";
                    }
                }
            } catch (Exception e) {
                status = "ERROR";
                banner = shortMsg(e.getMessage());
            }

            // sanitize and add to results
            String safeBanner = banner == null ? "" : banner.replace("\"", "\"\"");
            String csvLine = String.format("%s,%s,%d,%s,\"%s\"", timestamp, host, p, status, safeBanner);
            results.add(csvLine);
        });
    }

// 在所有 submit 完成後,再呼叫 shutdown(只呼叫一次)
pool.shutdown();
try {
// 等待所有任務完成(視需要可調整 timeout)
if (!pool.awaitTermination(30, TimeUnit.MINUTES)) {
pool.shutdownNow();
}
} catch (InterruptedException ie) {
pool.shutdownNow();
Thread.currentThread().interrupt();
}

// 之後把 results 寫入檔案(如果你已有寫入程式,保留原邏輯)
try (PrintWriter pw = new PrintWriter(new FileWriter(outFile))) {
while (!results.isEmpty()) {
pw.println(results.poll());
}
System.out.println("掃描完成,結果已寫入:" + outFile);
} catch (IOException e) {
System.err.println("寫入檔案失敗: " + e.getMessage());
}

}

private static String httpHead(String host, boolean https)throws Exception {
   String scheme = https ? "https" : "http";
   String urlStr=String.format("%s://%s",scheme,host);
   URL url = new URL(host);

   if(https){
       HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
       con.setConnectTimeout(TIMEOUT_MS);
       con.setReadTimeout(TIMEOUT_MS);
       con.setRequestMethod("HEAD");
       con.connect();
       String sever = con.getHeaderField("Sever");
       con.disconnect();
       return sever != null ? sever : "";
   }else{
       HttpURLConnection con = (HttpURLConnection) url.openConnection();
       con.setConnectTimeout(TIMEOUT_MS);
       con.setReadTimeout(TIMEOUT_MS);
       con.setRequestMethod("HEAD");
       con.connect();
       String sever = con.getHeaderField("Sever");
       con.disconnect();
       return sever != null ? sever : "";
   }

}


private static String timestampNow() {
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX");
    return sdf.format(new Date());
}
private static String shortMsg(String s) {
    if (s == null) return "";
    return s.length() <= 200 ? s.replace(",", " ") : s.substring(0,200).replace(",", " ");
}

}

程式實作學到的技巧:
args[] 的使用:從命令列讀取 host 和輸出檔案名稱。
try-catch:分別處理 IOException(Port 關閉)和其他 Exception(未知錯誤)。
Socket InputStream:讀伺服器的回應訊息。
三元運算子:server != null ? server : "" → 讓程式更簡潔。
HttpURLConnection / HttpsURLConnection:用 HEAD 請求拿 HTTP(S) 的 Server 標頭,快速判斷伺服器種類。
這正是滲透測試(Penetration Testing)或弱點掃描的基礎步驟:
掃 Port → 哪些 Port 開啟
服務辨識 → 這個 Port 跑什麼服務
版本探測 → 伺服器版本多少?有沒有漏洞?
今天練習的程式碼就完成第二步。

https://ithelp.ithome.com.tw/upload/images/20251015/20179429jgCQ3RWFrz.png

1.確認使用者給的資料
如果你沒告訴程式要掃描哪台電腦,它會說:Usage: java Day25Demo [output.csv]
告訴你要輸入電腦名稱或 IP,例如 scanme.nmap.org

2.建立一個「多工隊伍」
為了同時敲很多門,程式建立了「工作人員隊伍」(thread pool)
每個工作人員可以同時去敲一個 port

3.開始掃描 port
對每個 port:
嘗試連線
如果連上,記錄「開著」和服務資訊(banner)
如果連不上,記錄「關閉」
如果超時或出錯,記錄「錯誤」

4.收集掃描結果
所有 port 的結果都放在一個「結果列表」裡

5.寫入 CSV 檔
最後把結果寫成像 Excel 可以打開的表格:timestamp, host, port, status, banner
timestamp → 你掃描的時間
host → 哪台電腦
port → 哪扇門
status → 開 / 關 / 錯誤
banner → 服務名稱

6.結束程式
所有工作完成後,程式告訴你:
掃描完成,結果已寫入:result.csv

今天我學會了「服務辨識」的基礎觀念。
以前我只知道 Port 是開還是關,但現在我會去讀伺服器的 banner 或 HTTP header,來判斷它實際上跑什麼服務。
在程式裡我也學會用 Java 的 HttpURLConnection 與 HttpsURLConnection 發送 HEAD 請求,並且取出 Server 標頭,這讓我理解了 Nmap 或資安工具是怎麼辨識服務的。
這對未來學滲透測試、漏洞掃描很重要,因為要攻擊或防禦之前,必須先知道對方跑的是什麼程式。


圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言